package com.k8s;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;


public class ZTool {
    //工作模式，
    //0，监控模式【给出监控结果，】
    //1，分析模式【分析故障原因。】
    int workMode = 0;
    String timeString;
    int errorLimit = 0;
    //清零时间,
    int clearTime = 5;
    //5分钟错误计数器。
    int errorCount = 0;
    //5分钟超时数。
    int timeOutCount = 0;
    //超时配置
    int timeOut = 25;
    //
    //地址
    ArrayList urls = new ArrayList();
    //大于60秒接口。
    ArrayList<NetAction> timeOutList = new ArrayList();
    //50X接口。
    ArrayList<NetAction> code50xList = new ArrayList();
    //504接口。
    ArrayList<NetAction> code504List = new ArrayList();
    //error接口。
    ArrayList<NetAction> errorList = new ArrayList();
    //error接口。
    ArrayList errorListTemp = new ArrayList();
    //日志文件读取行数
    int readCount = 10000;
    //pods 列表
    static List<String> podsList = new ArrayList();
    //namespase
    static String nameSpace = "jcy-mall-dev";
    static String workDir = "";
    //过滤器，过滤字符串。
//	【只取proxy，接口相关请求】
    String filter = "";
    //本地临时文件列表
    ArrayList tempFileList = new ArrayList();
    SimpleDateFormat dateFormatNginx = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z", Locale.ENGLISH);
    SimpleDateFormat dateFormatPrint = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Integer max_Period = 60 * 1000 * 20;//20分支内，出错次数。
    Date AnalysisTime = new Date();
    boolean GET_FROM_POD = true;

    public static void main(String[] args) {
//		 先做工具。后做成任务。
//		 把日志通过命令取到本地。
//		遍历本地文件
//		读取错误的行
//		读取超时的行
//		插入行的时候，去掉5分钟前的行。

//		输入出现问题时间点。
//		不看超过时间点的数据结果即可。
//		输入参数，对参数进行解析。

        //处理模式：
//		1、分析模式，给出时间点。分析时间点以前的问题接口列表。
//		2、监控/巡检模式
//		java ZTool 15:18  分析模式 

        //at 分析时间
        //ad 工作目录
        //ns 是否重新拉取

        System.out.println("----------指定日期，指定处理目录，进行分析---------------------------------");
        System.out.println("java ZTool \"AT=2024-04-18 15:19:00\" AD=2024-04-28 NS=1 WS=0");
        System.out.println("----------AT 截止时间-----------------------------------------------------");
        System.out.println("----------AD 工作日期，工作目录，可以为空，处理当天数据。-------------------");
        System.out.println("----------NS 工作模式，为1 解决问题 时，AT生效。只处理截止时间前数据----------");
        System.out.println("----------WS 工作区，0，个人商城，1，企业商城-------------------------------");
        System.out.println("----------分析当前日期所有内容---------------------------------------------");
        System.out.println("java ZTool ");
        System.out.println();
        System.out.println();
        ZTool tool = new ZTool();
        Date now = new Date();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        tool.timeString = dateFormat.format(now);

        for (String s : args) {
            String[] argdata = s.split("=");
            if (argdata.length == 2) {
                if ("AT".equals(argdata[0])) {
                    try {
                        tool.AnalysisTime = tool.dateFormatPrint.parse(argdata[1]);
//						tool.workMode=1;
                    } catch (ParseException e) {

                    }
                }
                if ("AD".equals(argdata[0])) {
                    tool.timeString = argdata[1];
                }
                if ("NS".equals(argdata[0])) {
                    if ("0".equals(argdata[1])) {
                        tool.workMode = 0;
                    } else {
                        tool.workMode = 1;
                    }
                }
                if ("WS".equals(argdata[0])) {
                    if ("0".equals(argdata[1])) {
                        tool.nameSpace = "ygmall-prod";
                    }
                }
            }
        }
        podsList = getNginxPod();
        tool.init();
        tool.execute();
        //进行接口交互，进行深入分析。
    }

    //初始化变量
    public void init() {
        errorLimit = 100;
        workDir = "/appdata/temp/";
        //nameSpace="ygmall-prod";
        System.out.println();
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("win")) {
            // Windows操作系统
            workDir = "D:/Temp/418/0428/";
            GET_FROM_POD = false;
        } else if (os.contains("mac")) {
            // macOS操作系统
        } else if (os.contains("linux")) {
            // Linux操作系统
        } else if (os.contains("unix")) {
            // Unix操作系统
        } else {
            // 其他操作系统
        }
        dateFormatNginx.setTimeZone(TimeZone.getTimeZone("GMT+8"));
    }

    public void execute() {
//		 把日志通过命令取到本地。

        List<String> fileNames = getLogFromNginx();
//		遍历本地文件
//		parseFile("d:/temp/418/");
    }

    //把日志通过命令取到本地。
    private List<String> getLogFromNginx() {
        // workDir 为工作区
        // 判断有没有目录，如果没有，进行创建。
        String dirName = workDir + timeString + "/";
        System.out.println("工作目录：" + dirName);
        File filTemp = new File(dirName);

        if (GET_FROM_POD) {

            if (!filTemp.exists()) {
                filTemp.mkdirs();

            } else {
                //删除所有log文件。

                String files[] = filTemp.list();
                for (String fileName : files) {
                    if (fileName.endsWith(".log")) {
                        File logFile = new File(dirName + fileName);
                        logFile.delete();
                    }
                }
//				System.out.println("目录已存在，请重试。");
//				System.exit(0);
            }

            for (String s : podsList) {
                getFile(s, dirName);
            }
        } else {

        }
        parseFile(dirName);

        return null;
    }

    private void parseFile(String dirName) {

        File filTemp = new File(dirName);
        if (!filTemp.exists()) {
            System.exit(0);
        }
        ArrayList arr = new ArrayList();
        File[] files = filTemp.listFiles();
        //把所有对象放同一个数组。
        for (File f : files) {
            arr.addAll(doFile(f));
        }
        //对数组按照时间排序。
        arr.sort(new NACompareByStart());
        doAnalysis(arr);
    }

    private void doAnalysis(ArrayList<NetAction> arr) {
        // 对收集上的数据进行分析。
        // 认定问题的逻辑。
        // 1.超时请求列表
        // 2.500请求列表
        // 3.50X请求列表n个。
        // 4.所有接口的平均时间。
        // 对应的机器。----
        // 对应的操作人信息。
        // int timeoutFlag = 40;
        boolean errorFlag = false;
        errorList.clear();
        errorListTemp.clear();
        code504List.clear();
        code50xList.clear();
        timeOutList.clear();
        int errorCount = 0;
        int llPoint = 0;
        long ll = 9300000L;
        for (NetAction na : arr) {
            if (na.getTime() > timeOut) {
                errorFlag = true;
                timeOutList.add(na);
            }
            if (na.getStatus() >= 500 && na.getStatus() < 600) {
                errorFlag = true;
                code50xList.add(na);
            }
            if (na.getStatus() == 504) {
                errorFlag = true;
                code504List.add(na);
            }
            if (errorFlag) {
                errorList.add(na);
                errorCount++;
            }
            if (errorCount >= 2000) {
                System.out.println("问题过多！");
                break;
            }
            //连续20个错误请求的前后间隔时间..这个不准。要在timeOut里面计算。
            if (timeOutList.size() > 20 && errorFlag) {
                NetAction na2 = timeOutList.get(timeOutList.size() - 20);
                if (na2.getReqTime().getTime() - na.getReqTime().getTime() < ll) {
                    ll = na2.getReqTime().getTime() - na.getReqTime().getTime();
                    //最近间隔的点。
                    llPoint = timeOutList.size();
                }
            }
            errorFlag = false;
        }
//		printData(errorList.subList(llPoint-20, llPoint));
        System.out.println("---------------------------------------分隔符------------------------------");
        printData(code504List);
        System.out.println("---------------------------------------分隔符------------------------------");
        code50xList.removeAll(code504List);
        printData(code50xList);
        System.out.println("---------------------------------------分隔符------------------------------");
        printData(timeOutList);
        System.out.println("20个连续错误的最小时间----:" + ll);
        System.out.println("timeOutList.size()-------:" + timeOutList.size());
        System.out.println("code50xList.size()-------:" + code50xList.size());
        System.out.println("code504List.size()-------:" + code504List.size());
        System.out.println("errorList.size()---------:" + errorList.size());
        System.out.println("分析完毕！");

//TODO		最后处理这些。输出统计结果。
        //统计 分钟，小时，总量
//		根据排序，总时长，请求个数
        //最大，最小，平均，p50，p95，p99
//		根据排序，
//		最大值
//		最小值
//		平均值
//		前50%
//		前95%
//		前99%
        //成功率，4xx占比，5xx占比 。400个数，500个数 除总数。


    }

    private ArrayList doFile(File f) {
        //读取文件，放到list里面。
        ArrayList arr = new ArrayList();
        int count = 1;
        try {
            InputStreamReader reader = new InputStreamReader(
                    new FileInputStream(f));
            BufferedReader br = new BufferedReader(reader);
            String line = "";
//			line = br.readLine();
            while (line != null) {
                if (!"".equals(line)) {
                    NetAction na = parseNetAction(line);
                    //只处理大于1，状态不是200的。
                    if (na.status != 200 || na.getTime() > 1) {
                        //跳过此行
                        //}else{
                        if (this.workMode == 1) {//分析模式不处理故障后的请求。
                            if (this.AnalysisTime.getTime() > na.getReqTime().getTime()) {
                                arr.add(na);
                            }
                        } else {
                            arr.add(na);
                        }
                    }
                }
                line = br.readLine(); // 一次读入一行数据
                count++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("文件[ " + f.getName() + " ]读取：" + count + "行！有效数据" + arr.size() + "行！");
        return arr;
    }

    private NetAction parseNetAction(String data) {
//		System.out.println(data);
        NetAction na = new NetAction();
        String arr[] = data.split("\\|\\|");
        na.setMethod(arr[5]);
        na.setOrgion(arr[13]);
        na.setParam(arr[7]);
        na.setStatus(Integer.parseInt(arr[9]));
        if (na.getStatus() >= 400 && na.getStatus() < 500) {
            na.setError(true);
            na.setIs40X(true);
        }
        if (na.getStatus() >= 500 && na.getStatus() < 600) {
            na.setError(true);
            na.setIs50X(true);
        }

        na.setTime(Float.parseFloat(arr[12]));
        if (na.getTime() >= timeOut) {
            na.setError(true);
        }
        try {
            if (arr[18].equals("-")) {
                na.setTime2(0);
            } else {
                na.setTime2(Float.parseFloat(arr[18]));
            }
        } catch (Exception e) {
            System.out.println(data);
            e.printStackTrace();
        }

        na.setUrl(arr[6]);
        na.setRequestBodyLength(Integer.parseInt(arr[10]));
        na.setResponseBodyLength(Integer.parseInt(arr[11]));
        try {
            Date date = dateFormatNginx.parse(arr[4]);
//            System.out.println(date);
            na.setReqTime(date);
        } catch (ParseException e) {

            e.printStackTrace();
        }

        return na;
    }

    private void getFile(String s, String dirName) {

        //执行命令
        try {
            String command = "kubectl cp " + nameSpace + "/" + s + ":/export/servers/nginx/logs/mall_access_" + timeString + ".log " + dirName + "nginx-" + s + ".log"; // 要执行的Linux命令 //组装命令
            System.out.println(command);
            Process process = Runtime.getRuntime().exec(command);

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            reader.close();
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void printData(List<NetAction> errorList2) {
        // TODO Auto-generated method stub
        for (NetAction data : errorList2) {

            appendInfoToFile("d:/abc.txt", data.getUrl() + ":" + dateFormatPrint.format(data.getReqTime()) + "---" + data.getTime());
            System.out.println(data.getUrl() + ":" + dateFormatPrint.format(data.getReqTime()) + "---" + data.getTime());
        }
    }

    public static void appendInfoToFile(String fileName, String info) {
        File file = new File(fileName);
        try {
            if (!file.exists()) {
                file.createNewFile();
            }
            FileWriter fileWriter = new FileWriter(file, true);
            info = info + ";" + System.getProperty("line.separator");
            fileWriter.write(info);
            fileWriter.flush();
            fileWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //从文件获取pod的名称。
    public static List<String> getNginxPod() {
        String fileName = workDir + nameSpace + ".txt";
        try {
            List<String> ls = new ArrayList();
            File filename = new File(fileName);
            InputStreamReader reader = new InputStreamReader(new FileInputStream(filename));
            BufferedReader br = new BufferedReader(reader);
            br.readLine();//丢弃第一行，第一行有特殊字符。
            String line = null;
            while ((line = br.readLine()) != null) {
                if (line.length() > 2) {
                    ls.add(line);
                }
            }
            return ls;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ArrayList();
    }

    private String getPodNames(String url) {
        String serviceName = "";
        if (url.startsWith("/api-oauth2-service/")) {
            serviceName = "api-oauth2";
        }
        if (url.startsWith("/item-center/")) {
            serviceName = "item-center";
        }
        if (url.startsWith("/price-center/")) {
            serviceName = "price-center";
        }
//		if(url.startsWith("")){
//			serviceName = "service-gateway";
//		}
        if (url.startsWith("/item/")) {
            serviceName = "service-item";
        }
        if (url.startsWith("/app/")) {
            serviceName = "service-mobile";
        }
        if (url.startsWith("/order/")) {
            serviceName = "service-order";
        }
        if (url.startsWith("/platform/")) {
            serviceName = "service-platform";
        }
        if (url.startsWith("/refund/")) {
            serviceName = "service-refund";
        }
        if (url.startsWith("/settlement/")) {
            serviceName = "service-settlement";
        }
        if (url.startsWith("/sourcing-service/")) {
            serviceName = "service-sourcing";
        }
        if (url.startsWith("/statistic-service/")) {
            serviceName = "service-statistic";
        }
        if (url.startsWith("/user/")) {
            serviceName = "service-user";
        }
//		if(url.startsWith("")){
//			serviceName = "service-worker";
//		}
//		if(url.startsWith("")){
//			serviceName = "service-workflow";
//		}
        if (url.startsWith("/supplier-center/")) {
            serviceName = "supplier-center";
        }
        if (url.startsWith("/supplier-service/")) {
            serviceName = "supplier-service";
        }
        if ("".equals(serviceName)) {
            System.out.println(url);
        }

        return serviceName;
    }

    class NetAction {
        //请求时间
        Date reqTime;
        //请求地址
        String url;
        //请求方法
        String method;
        //参数
        String param;
        //状态吗
        int status;
        //时间1
        float time;
        //时间2
        float time2;
        //来源
        String orgion;
        boolean is40X;
        boolean is50X;
        boolean isError;

        int requestBodyLength;
        int responseBodyLength;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getMethod() {
            return method;
        }

        public void setMethod(String method) {
            this.method = method;
        }

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }

        public int getStatus() {
            return status;
        }

        public void setStatus(int status) {
            this.status = status;
        }

        public float getTime() {
            return time;
        }

        public void setTime(float time) {
            this.time = time;
        }

        public float getTime2() {
            return time2;
        }

        public void setTime2(float time2) {
            this.time2 = time2;
        }

        public String getOrgion() {
            return orgion;
        }

        public void setOrgion(String orgion) {
            this.orgion = orgion;
        }

        public int getRequestBodyLength() {
            return requestBodyLength;
        }

        public void setRequestBodyLength(int requestBodyLength) {
            this.requestBodyLength = requestBodyLength;
        }

        public int getResponseBodyLength() {
            return responseBodyLength;
        }

        public void setResponseBodyLength(int responseBodyLength) {
            this.responseBodyLength = responseBodyLength;
        }

        public Date getReqTime() {
            return reqTime;
        }

        public void setReqTime(Date reqTime) {
            this.reqTime = reqTime;
        }

        public boolean isIs40X() {
            return is40X;
        }

        public void setIs40X(boolean is40x) {
            is40X = is40x;
        }

        public boolean isIs50X() {
            return is50X;
        }

        public void setIs50X(boolean is50x) {
            is50X = is50x;
        }

        public boolean isError() {
            return isError;
        }

        public void setError(boolean isError) {
            this.isError = isError;
        }

    }

    class NACompareByStart implements Comparator<NetAction> {

        @Override
        public int compare(NetAction arg0, NetAction arg1) {
            if (arg0 == null) {
                return 1;
            }
            if (arg1 == null) {
                return -1;
            }
            if (arg0.getReqTime() == null) {
                return 1;
            }
            if (arg1.getReqTime() == null) {
                return -1;
            }
            if (arg0.getReqTime().before(arg1.getReqTime())) {
                return -1;
            } else {
                if (arg0.getReqTime().getTime() == arg1.getReqTime().getTime()) {
                    return 0;
                }
                return 1;
            }

        }

    }

    class NACompareByTime implements Comparator<NetAction> {

        @Override
        public int compare(NetAction arg0, NetAction arg1) {
            if (arg0 == null) {
                return -1;
            }
            if (arg1 == null) {
                return 1;
            }
            if (arg0.getTime() == 0) {
                return -1;
            }
            if (arg1.getTime() == 0) {
                return 1;
            }
            if ((arg0.getTime() - arg1.getTime()) > 0) {
                return 1;
            } else {
                return -1;
            }
        }
    }
}
